package odfconverter;

import java.util.ArrayList;
import java.util.Collections;

public class AbstractPostProcessor extends XmlWriter {

	protected XmlWriter nextWriter;

    protected AbstractPostProcessor(XmlWriter nextWriter) throws Exception
    {
        this.nextWriter = nextWriter;

        if (nextWriter == null)
        {
            throw new Exception("nextWriter can's be null");
        }
    }
	@Override
	public void WriteEndAttribute() {
		this.nextWriter.WriteEndAttribute();
	}

	@Override
	public void WriteEndElement() {
		this.nextWriter.WriteEndElement();
	}

	@Override
	public void WriteStartAttribute(String prefix, String localName, String ns) {
		this.nextWriter.WriteStartAttribute(prefix, localName, ns);
	}

	@Override
	public void WriteStartElement(String prefix, String localName, String ns) {
		this.nextWriter.WriteStartElement(prefix, localName, ns);
	}

	@Override
	public void WriteString(String text) {
		this.nextWriter.WriteString(text);
	}

	protected class Node {
		private String name, prefix, ns;

		public String getName() {
			return name;
		}

		public void setName(String name) {
			this.name = name;
		}

		public String getPrefix() {
			return prefix;
		}

		public void setPrefix(String prefix) {
			this.prefix = prefix;
		}

		public String getNs() {
			return ns;
		}

		public void setNs(String ns) {
			this.ns = ns;
		}

		public Node(String prefix, String name, String ns) {
			this.prefix = prefix;
			this.name = name;
			this.ns = ns;
		}

		public Node(Node node) {
			this.prefix = node.prefix;
			this.name = node.name;
			this.ns = node.ns;
		}
	}

    protected class Attribute extends Node
    {
        private String value;

        public Attribute(String prefix, String name, String ns)            
        {
        	super(prefix, name, ns);
            this.value = "";
        }

        public Attribute(String prefix, String name, String val, String ns)
        {
        	super(prefix, name, ns);
            this.value = val;
        }

        public Attribute(Node node)
        {
        	super(node);
            this.value = "";
        }

        public String getValue() {
			return value;
		}

		public void setValue(String value) {
			this.value = value;
		}

		public void Write(XmlWriter writer)
        {//是否需要写成writeCharacter？
            writer.WriteStartAttribute(this.getPrefix(), this.getName(), this.getNs());
            writer.WriteString(this.value);
            writer.WriteEndAttribute();
        }
    }

    protected class Element extends Node implements Comparable
    {
        private ArrayList attributes;
        private ArrayList children;

        public Element(String prefix, String name, String ns)
        {
        	super(prefix, name, ns);
            this.attributes = new ArrayList();
            this.children = new ArrayList();
        }

        public Element(Node node)
        {
        	super(node);
            this.attributes = new ArrayList();
            this.children = new ArrayList();
        }

		@Override
		public int compareTo(Object obj) {
			// TODO Auto-generated method stub
            if (obj instanceof Element)
            {
                Element el = (Element)obj;
                return this.getName().compareTo(el.getName());
            }
            else if (obj instanceof String)
            {
                String s = (String)obj;
                return this.getName().compareTo(s);
            }
            return 1;
		}

		public void AddAttribute(Attribute attribute)
        {
            this.attributes.add(attribute);
        }

        public ArrayList getAttributes() {
			return attributes;
		}

		public void setAttributes(ArrayList attributes) {
			this.attributes = attributes;
		}

		public ArrayList getChildren() {
			return children;
		}

		public void setChildren(ArrayList children) {
			this.children = children;
		}

		public void RemoveAttribute(Attribute attribute)
        {
            if (this.attributes.contains(attribute))
            {
                this.attributes.remove(attribute);
            }
        }

        public void AddChild(Object child)
        {
            this.children.add(child);
        }

        public void AddFirstChild(Object element)
        {
            this.children.add(0, element);
        }

        public void AddFirstChild(Element element)
        {
            this.children.add(0, element);
        }

        public void AddChildIfNotExist(Element element)
        {
            if (this.GetChild(element.getName(), element.getNs()) == null)
            {
                this.children.add(element);
            }
        }

        public void AddChild(String text)
        {
            this.children.add(text);
        }

        public void RemoveChild(Object child)
        {
            if (this.children.contains(child))
            {
                this.children.remove(child);
            }
        }

        public Element GetChild(String name, String ns)
        {
            for (Object node :this.children)
            {
                if (node instanceof Element)
                {
                    Element element = (Element)node;
                    if (element.getName().equals(name) && element.getNs().equals(ns))
                    {
                        return element;
                    }
                }
            }
            return null;
        }

        public String GetTextChild()
        {
        	for (Object node : this.children)
        	{
        		if(node instanceof String){
        			String child = (String)node;
        			return child;
        		}
        	}
        	return "";
        }
        
        public Element GetChildWithAttribute(String name, String ns, String attrName, String attrNs, String attrValue)
        {
        	for(Object node : this.children)
        	{
        		if(node instanceof Element)
        		{
        			Element element = ((Element)node);
        			if (element.getName().equals(name) && element.getNs().equals(ns) && attrValue.equals(element.GetAttributeValue(attrName, attrNs)))
                    {
                        return element;
                    }
        		}
        	}
        	return null;
        }

        public ArrayList GetChildren(String name, String ns)
        {
        	ArrayList elements = new ArrayList();
        	
        	for (Object node : this.children)
        	{
        		if (node instanceof Element)
        		{
        			Element element = (Element)node;
        			if (element.getName().equals(name) && element.getNs().equals(ns))
        			{
        				elements.add(element);
        			}
        		}
        	}
        	return elements;
        }

        
        public ArrayList<Element> GetChildElements()
        {
        	ArrayList<Element> elements = new ArrayList<Element>();
        	
        	for (Object node : this.children)
        	{
        		if (node instanceof Element)
        		{
        			Element element = (Element)node;
        			elements.add(element);
        		}
        	}
        	return elements;
        }

		public ArrayList GetTextChildren()
        {
        	ArrayList elements = new ArrayList();
        	
        	for (Object node : this.children)
        	{
        		if (node instanceof String)
        		{
        			String element = (String)node;
        			elements.add(element);
        		}
        	}
        	return elements;	
        }
        
        public ArrayList GetChildElementsWithText()
        {
        	ArrayList elements = new ArrayList();
        	
        	for (Object node : this.children)
        	{
        		if (node instanceof Element)
        		{
        			Element element = (Element)node;
        			if(element.GetTextChild()!=null){
        				elements.add(element);
        			}
        		}
        	}
        	return elements;
        }
        
        public Attribute GetAttribute(String name, String ns)
        {
            for (Object attribute1 : this.attributes)
            {
            	if (attribute1 instanceof Attribute)
            	{
            		Attribute attribute = (Attribute)attribute1;
                    if (attribute.getName().equals(name) && attribute.getNs().equals(ns))
                    {
                        return attribute;
                    }
            	}
            }
            return null;
        }

        public String GetAttributeValue(String name, String ns)
        {
            for (Object obj : this.attributes)
            {
            	if (obj instanceof Attribute)
            	{
            		Attribute attribute = (Attribute)obj;
                    if (attribute.getName().equals(name) && attribute.getNs().equals(ns))
                    {
                        return attribute.getValue();
                    }
            	}
            }
            return "";
        }

        public void SortChildren()
        {
        	Collections.sort(this.children);  
            //this.children.Sort();
        }

        public Boolean HasChild()
        {
            return this.children.size() > 0;
        }

		public Boolean HasElement()
        {
        	return this.GetChildElements().size() > 0;
        }
        
        public Boolean HasElementWithText()
        {
        	return this.GetChildElementsWithText().size() > 0;
        }

        public Boolean IsEqualTo(Element e)
        {
            if (e == null) return false;
            for (Object obj : this.attributes)
            {
            	if(obj instanceof Attribute)
            	{
            		Attribute attribute = (Attribute)obj; 
            		String value = attribute.getValue();
                    String comparedValue = e.GetAttributeValue(attribute.getName(), attribute.getNs());
                    if ((value == null || "".equals(value)) && comparedValue != null && !"".equals(comparedValue))
                    {
                        return false;
                    }
                    if (!value.equals(comparedValue))
                    {
                        return false;
                    }
            	}
            	
                
            }
            for (Object obj : e.attributes)
            {
            	if(obj instanceof Attribute)
            	{
            		Attribute attribute = (Attribute)obj; 
                    String value = attribute.getValue();
                    String comparedValue = e.GetAttributeValue(attribute.getName(), attribute.getNs());
                    if ((value == null || "".equals(value)) && comparedValue != null && !"".equals(comparedValue))
                    {
                        return false;
                    }
                    if (!value.equals(comparedValue))
                    {
                        return false;
                    }
            	}
            }
            return true;
        }

        public void Replace(Object child, Object newChild)
        {
            // replace a string child
            if ((child instanceof String) && (newChild instanceof String))
            {
                if (this.GetTextChildren().contains(child))
                {
                    int index = this.children.indexOf(child);
                    this.children.remove(child);
                    this.children.add(index, newChild);
                }
            }
            // replace an attribute child
            else if ((((Attribute)child) != null) && (((Attribute)newChild) != null))
            {
                if (this.attributes.contains(child))
                {
                    int index = this.attributes.indexOf(child);
                    this.attributes.remove(child);
                    this.attributes.add(index, newChild);
                }
            }
            // replace an element child
            else if (this.children.contains(child))
            {
                int index = this.children.indexOf(child);
                this.children.remove(child);
                this.children.add(index, newChild);
            }
        }

        public void Write(XmlWriter writer) throws Exception
        {
            writer.WriteStartElement(this.getPrefix(), this.getName(), this.getNs());
            for (Object obj : this.attributes)
            {
            	if(obj instanceof Attribute)
            	{
            		Attribute attribute = (Attribute)obj;
            		attribute.Write(writer);
            	}                
            }
            for (Object node : this.children)
            {
                if (node instanceof Element)
                {
                    ((Element)node).Write(writer);
                }
                else if (node instanceof String)
                {
                    writer.WriteString((String)node);
                }
                else
                {
                    throw new Exception("Child node must be of type Element or string");
                }
            }
            writer.WriteEndElement();
        }

        public Element Clone()
        {
            Element result = new Element(this);
            // copy attributes
            for (Object attr : this.attributes)
            {
                Attribute attr2 = new Attribute((Attribute)attr);
                
                attr2.setValue(((Attribute)attr).getValue());
                result.AddAttribute(attr2);
            }
            // copy children
            for (Object obj : this.children)
            {
                if (obj instanceof Element)
                {
                    Element child = (Element)obj;
                    result.AddChild(child.Clone());
                }
                else if (obj instanceof String)
                {
                    String child = (String)obj;
                    result.AddChild(new String(child.toString()));//clone？
                }
            }
            return result;
        }
        
		private class tStringClone implements Cloneable {
			//字符串变量的clone实现，暂放弃
			private String value;

			public void tStringClone(String s) {
				this.value = s;
			}

			public void setvalue(String s) {
				this.value = s;
			}

			public String getvalue() {
				return this.value;
			}

			public Object clone() {
				tStringClone o = null;
				try {
					o = (tStringClone) super.clone();
				} catch (CloneNotSupportedException e) {
					e.printStackTrace();
				}
				return o;
			}
		}
	}
}
